package com.git.hui.yinshua.web.filter;


import com.git.hui.yinshua.api.model.ReqInfoContext;
import com.git.hui.yinshua.biz.user.service.UserSessionService;
import com.git.hui.yinshua.biz.util.MdcUtil;
import com.git.hui.yinshua.common.SessionUtil;
import com.git.hui.yinshua.common.SpringUtil;
import com.git.hui.yinshua.web.util.CrossUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;

/**
 * @author YiHui
 * @date 2024/6/7
 */
@Slf4j
@WebFilter(urlPatterns = "/*", filterName = "selfProcessBeforeFilter", asyncSupported = true)
public class InitUserInfoProcessBeforeFilter implements Filter {
    private static Logger reqLog = LoggerFactory.getLogger("req");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
        HttpServletRequest request = null;
        try {
            MdcUtil.addTraceId();
            request = this.initReqInfo((HttpServletRequest) servletRequest);
            if (ReqInfoContext.getReqInfo() != null && ReqInfoContext.getReqInfo().getUserAgent() != null && ReqInfoContext.getReqInfo().getUserAgent().startsWith("WordPress")) {
                // 非法的ua
                return;
            }
            CrossUtil.buildCors(request, (HttpServletResponse) servletResponse);
            filterChain.doFilter(request, servletResponse);
        } finally {
            buildRequestLog(ReqInfoContext.getReqInfo(), request, System.currentTimeMillis() - start);
            MdcUtil.clear();
            ReqInfoContext.clear();
        }
    }

    @Override
    public void destroy() {
    }

    private HttpServletRequest initReqInfo(HttpServletRequest request) {
        try {
            ReqInfoContext.ReqInfo reqInfo = new ReqInfoContext.ReqInfo();
            reqInfo.setHost(request.getHeader("host"));
            reqInfo.setPath(request.getPathInfo());
            reqInfo.setReferer(request.getHeader("referer"));
            reqInfo.setClientIp(getClientIP(request));
            reqInfo.setUserAgent(request.getHeader("User-Agent"));
            reqInfo.setSessionId(SessionUtil.getSessionId(request, UserSessionService.SESSION_COOKIE_KEY));
            if (reqInfo.getSessionId() != null) {
                reqInfo.setUser(SpringUtil.getBean(UserSessionService.class).getUserVoBySessionId(reqInfo.getSessionId()));
                if (reqInfo.getUser() != null) {
                    // userId提出来，因为用的地方很多
                    reqInfo.setUserId(reqInfo.getUser().getUserId());
                }
            }
            request = this.wrapperRequest(request, reqInfo);
            ReqInfoContext.addReqInfo(reqInfo);
        } catch (Exception e) {
            log.error("init reqInfo error!", e);
        }

        return request;
    }

    private void buildRequestLog(ReqInfoContext.ReqInfo req, HttpServletRequest request, long costTime) {
        if (request == null || request.getRequestURI().endsWith("css") || request.getRequestURI().endsWith("js") || request.getRequestURI().endsWith("png") || request.getRequestURI().endsWith("ico")) {
            return;
        }

        if (req == null) {
            return;
        }

        StringBuilder msg = new StringBuilder();
        msg.append("method=").append(request.getMethod()).append("; ");
        if (StringUtils.isNotBlank(req.getReferer())) {
            msg.append("referer=").append(URLDecoder.decode(req.getReferer())).append("; ");
        }
        msg.append("remoteIp=").append(req.getClientIp());
        msg.append("; agent=").append(req.getUserAgent());

        if (req.getUserId() != null) {
            // 打印用户信息
            msg.append("; user=").append(req.getUserId());
        }

        msg.append("; uri=").append(request.getRequestURI());
        if (StringUtils.isNotBlank(request.getQueryString())) {
            msg.append('?').append(URLDecoder.decode(request.getQueryString()));
        }

        msg.append("; payload=").append(req.getPayload());
        msg.append("; cost=").append(costTime);
        reqLog.info("{}", msg);
    }

    private static String getClientIP(HttpServletRequest request) {
        try {
            String Xip = request.getHeader("X-Real-IP");
            String XFor = request.getHeader("X-Forwarded-For");
            if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
                //多次反向代理后会有多个ip值，第一个ip才是真实ip
                int index = XFor.indexOf(",");
                if (index != -1) {
                    return XFor.substring(0, index);
                } else {
                    return XFor;
                }
            }
            XFor = Xip;
            if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
                return XFor;
            }
            if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
                XFor = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
                XFor = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
                XFor = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
                XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
                XFor = request.getRemoteAddr();
            }
            return XFor;
        } catch (Exception e) {
            log.error("get remote ip error!", e);
            return "x.0.0.1";
        }
    }


    private HttpServletRequest wrapperRequest(HttpServletRequest request, ReqInfoContext.ReqInfo reqInfo) {
        if (!"POST".equalsIgnoreCase(request.getMethod())) {
            return request;
        }

        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
        reqInfo.setPayload(requestWrapper.getBodyString());
        return requestWrapper;
    }

}
